package com.averroes
{
	public class ArrayUtils
	{
		/**
		 * Returns the intersection of two arrays.
		 * 
		 * @param a
		 * @param b
		 * @return 
		 * 
		 */		
		public static function array_intersect(a:Array, b:Array, tolerance:int=0):Array
		{
			//@todo: Make this more efficient by sorting both arrays.
			var ret:Array = new Array();
			for (var i:int=0; i<a.length; i++)
				for (var j:int=0; j<b.length; j++)
					if (a[i] <= b[j]+tolerance &&  a[i] >= b[j]-tolerance)
						ret.push(a[i]);
			return ret;
		}
		
		/**
		 * Combines two arrays. 
		 * @param a
		 * @param b
		 * @return 
		 * 
		 */		
		public static function array_combine(a:Array, b:Array):Array
		{
			var c:Array = new Array(a.length + b.length);
			for (var i:int=0; i<a.length; i++)
				c[i] = a[i];
			for (var j:int=0; j<b.length; j++)
				c[i+j] =b[j];
			return c;
		}
		
		/**
		 * Returns a copy of the given array
		 * @param a
		 * @return 
		 * 
		 */		
		public static function array_copy(a:Array):Array
		{
			return a.slice();
		}
		
		public static function array_remove_slice(array:Array, startIndex:int=0, endIndex:int=16777215):Array
		{
			var ret:Array;
			ret = array.slice(0, startIndex).concat(array.slice(endIndex));
			return ret;
		}
		
		/**
		 * Removes duplicate values in an array.
		 * @param array
		 * @return 
		 * 
		 */		
		public static function array_unique(array:Array):Array
		{
			if (array.length == 0)
				return array;
			var copy:Array = array_copy(array);
				copy.sort(Array.NUMERIC);
				
			var ret:Array = new Array();
			var i:int=0;
			for (var j:int=0; i<copy.length && j<=copy.length; j++) {
				while(copy[i] == copy[j]) {
					j++;
				}
				ret.push(copy[i]);
				i = j;					
			}
			return ret;
		}

		/**
		 * Returns the indices of array items matching the needle.
		 * @param haystack
		 * @param needle
		 * @return 
		 * 
		 */	
		public static function find(haystack:Array, needle:*):Array
		{
			var ret:Array = new Array();
			//this should be more efficient
			for (var i:int=0; i<haystack.length; i++)
				if (haystack[i] == needle)
					ret.push(i);
			return ret;
		}

		/**
		 * Returns the indices of points the values of which are less than the given threshold
		 * For example:
		 * array = [1 2 3 4 5 6 7 8 9]
		 * getEntriesBelow(array, 4) will return [1 2 3]
		 * @param array
		 * @param threshold
		 * @return 
		 * 
		 */		
		public static function findLessThanOrEqual(array:Array, threshold:Number):Array
		{
			var ret:Array = new Array();
			for (var i:int=0; i<array.length; i++)
				if (array[i] <= threshold)
					ret.push(i);
			return ret;
		}

		public static function findGeq(array:Array, threshold:Number):Array
		{
			var ret:Array = new Array();
			for (var i:int=0; i<array.length; i++)
				if (array[i] >= threshold)
					ret.push(i);
			return ret;
		}
		
		public static function findNotEqual(array:Array, threshold:Number):Array
		{
			var ret:Array = new Array();
			for (var i:int=0; i<array.length; i++)
				if (array[i] != threshold)
					ret.push(i);
			return ret;			
		}
		
		public static function findEqual(array:Array, threshold:Number):Array
		{
			return find(array, threshold);
		}
				
		public static function find_conditional(haystack:Array, rule:String):Array
		{
			return new Array();
		}
		
		/**
		 * ret[i] = array[i] + value;
		 * @param array
		 * @param value
		 * @return 
		 * 
		 */		
		public static function add(array:Array, value:Number):Array
		{
			var ret:Array = new Array();
			for (var i:int=0; i<array.length; i++)
				ret[i] = array[i] + value;
			return ret;
		}
		
		/**
		 * ret[i] = array[i] * value;
		 * @param array
		 * @param value
		 * @return 
		 * 
		 */		
		public static function multiply(array:Array, value:Number, floor:Boolean=false):Array
		{
			var ret:Array = new Array();
			for (var i:int=0; i<array.length; i++)
				if (floor)
					ret[i] = Math.floor(array[i] * value);
				else
					ret[i] = array[i] * value;
			return ret;
		}		
		
		/**
		 * ret[i] = a[i] + b[i]
		 * @param a
		 * @param b
		 * @return 
		 * 
		 */		
		public static function add_arrays(a:Array, b:Array):Array
		{
			var ret:Array = new Array();
			for (var i:int=0; i<a.length && i<b.length; i++)
				ret[i] = a[i] + b[i];
			return ret;
		}
		
		/**
		 * ret[i] = a[i] - b[i];
		 * @param a
		 * @param b
		 * @return 
		 * 
		 */		
		public static function subtract_arrays(a:Array, b:Array):Array
		{
			var ret:Array = new Array();
			for (var i:int=0; i<a.length && i<b.length; i++)
				ret[i] = a[i] - b[i];
			return ret;
		}
		
		/**
		 * calculates the absolute value element wise
		 * @param a
		 * @return 
		 * 
		 */		
		public static function abs(a:Array):Array
		{
			var ret:Array = array_copy(a);
			for (var i:int=0; i<ret.length; i++)
				ret[i] = Math.abs(ret[i]);
			return ret;
		}
		
		/**
		 * Returns the values in the array denoted by the indices array.
		 * @param a
		 * @param indices
		 * @return 
		 * 
		 */		
		public static function values(a:Array, indices:Array):Array
		{
			var ret:Array = new Array();
			for (var i:int = 0; i<indices.length; i++)
				ret[i] = a[indices[i]];
			return ret;
		}
		public static function extreme(array:Array):Array
		{
			var ret:Array;
			//find the derivative
			var dx:Array = Calculus.diff(array);
			//indices where the first derivative changes //a
			var arrChangeInd:Array = findNotEqual(dx, 0);
			//indices of where arrChangeInd does not change //lm
			var arrNotChangedInd:Array = findNotEqual(Calculus.diff(arrChangeInd),1);
			//elements in the flat peaks //d
			var flatPeaks:Array = add_arrays(values(arrChangeInd, arrNotChangedInd), values(arrChangeInd, add(arrNotChangedInd, -1)));
			
			
			return ret;
		}					
	}
}